package br.unicamp.ic.lis.dbvapi.controller.impl;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import br.unicamp.ic.lis.dbvapi.controller.APIController;
import br.unicamp.ic.lis.dbvapi.domain.DBV;
import br.unicamp.ic.lis.dbvapi.domain.LogicalVersion;
import br.unicamp.ic.lis.dbvapi.domain.MultiversionObject;
import br.unicamp.ic.lis.dbvapi.domain.Scale;
import br.unicamp.ic.lis.dbvapi.service.DBVService;
import br.unicamp.ic.lis.dbvapi.service.LogicalVersionService;
import br.unicamp.ic.lis.dbvapi.service.MultiversionObjectService;
import br.unicamp.ic.lis.dbvapi.service.PhysicalVersionService;
import br.unicamp.ic.lis.dbvapi.service.ScaleService;
import br.unicamp.ic.lis.dbvapi.service.impl.DBVServiceImpl;
import br.unicamp.ic.lis.dbvapi.service.impl.LogicalVersionServiceImpl;
import br.unicamp.ic.lis.dbvapi.service.impl.MultiversionObjectServiceImpl;
import br.unicamp.ic.lis.dbvapi.service.impl.PhysicalVersionServiceImpl;
import br.unicamp.ic.lis.dbvapi.service.impl.ScaleServiceImpl;
import br.unicamp.ic.lis.dbvapi.utils.ServiceUtils;

public class APIControllerImpl implements APIController {

	private static Logger LOG = Logger.getLogger(APIController.class);

	// services
	private MultiversionObjectService multiversionObjectService = new MultiversionObjectServiceImpl();
	private PhysicalVersionService physicalVersionService = new PhysicalVersionServiceImpl();
	private ScaleService scaleService = new ScaleServiceImpl();
	private DBVService dbvService = new DBVServiceImpl();
	private LogicalVersionService logicalVersionService = new LogicalVersionServiceImpl();

	@Override
	public void addMultiversionObject(MultiversionObject... o) {
		LOG.info("Adding multiversion object(s): " + o);
		multiversionObjectService.getDAO().persist(o);
	}

	@Override
	public void addScale(Scale... scale) {
		LOG.info("Adding scale(s): " + scale);
		scaleService.getDAO().persist(scale);
		for (Scale s : scale) {
			createDerivationTree(s);
		}
	}

	private DBV createDerivationTree(Scale scale) {
		LOG.info("Creating a derivation tree for scale: " + scale);
		DBV rootDBV = new DBV(null, scale);
		dbvService.getDAO().persist(rootDBV);
		return rootDBV;
	}

	@Override
	public void addLogicalVersion(LogicalVersion lv) {
		logicalVersionService.add(lv);
	}

	@Override
	public void updateLogicalVersion(LogicalVersion lv) {
		logicalVersionService.update(lv);
	}

	@Override
	public void removeLogicalVersion(LogicalVersion lv) {
		logicalVersionService.remove(lv);
	}

	@Override
	public List<LogicalVersion> getLogicalVersions(DBV dbv) {
		return logicalVersionService.getLogicalVersions(dbv);
	}

	@Override
	public List<DBV> getScenario(String stamp) {
		LOG.info("Getting scenario with stamp: " + stamp);
		return dbvService.getDBVs(stamp);
	}

	@Override
	public DBV createDBVFrom(String parentStamp, Scale scale) {
		DBV parentDBV = dbvService.getDBV(parentStamp, scale.getId());
		return createDBVFrom(parentDBV);
	}

	@Override
	public DBV createDBVFrom(DBV dbv) {
		List<DBV> parentScenario = dbvService.getDBVs(dbv.getStamp());
		List<DBV> newScenario = createNewScenario(parentScenario);
		for (DBV d : newScenario) {
			if (d.getParent().equals(dbv)) {
				return d;
			}
		}
		throw new RuntimeException(
				"Internal error: cannot be found DBV created from " + dbv);
	}

	private List<DBV> createNewScenario(List<DBV> parentScenario) {
		List<DBV> newScenario = new ArrayList<DBV>();
		if (parentScenario == null) {
			List<Scale> dimensions = scaleService.getDAO().findAll();
			for (Scale dimension : dimensions) {
				DBV dbv = new DBV(null, dimension);
				newScenario.add(dbv);
			}
		} else {
			for (DBV current : parentScenario) {
				DBV dbv = new DBV(current);
				newScenario.add(dbv);
			}
		}

		dbvService.getDAO().persist(newScenario);
		return newScenario;
	}

	@Override
	public void close() {
		ServiceUtils.close(multiversionObjectService, physicalVersionService,
				scaleService, dbvService, logicalVersionService);
	}

	@Override
	public MultiversionObjectService getMVOService() {
		return multiversionObjectService;
	}

	@Override
	public PhysicalVersionService getPVService() {
		return physicalVersionService;
	}

	@Override
	public ScaleService getScaleService() {
		return scaleService;
	}

	@Override
	public DBVService getDBVService() {
		return dbvService;
	}

	@Override
	public LogicalVersionService getLVService() {
		return logicalVersionService;
	}

	// ------------------------------------------------------------------------------------

	/*
	 * private static final String COMMITING_WORKSPACE_WITH_STAMP =
	 * "Commiting scenario with stamp: "; private static final String
	 * WORKSPACE_ALREADY_COMMITED = "Scenario already commited"; private static
	 * final String THIS_METHOD_MUST_BE_USED_TO_SET_A_WORKSPACE_TO_BE_PERSISTED
	 * = "This method must be used to set a scenario to be persisted";
	 * 
	 * private static final String YOU_MUST_PERSIST_THIS_DBV_BEFORE =
	 * "You must persist this DBV before: "; private static final String
	 * SCENARIO_NULL = "Scenario null"; private static final String
	 * WORKSPACE_NOT_FOUND = "Scenario not found"; private static final String
	 * YOU_MUST_SET_CURRENT_WORKSPACE_FIRST =
	 * "You must set the current scenario first";
	 */

	/*
	 * public void setCurrentScenario(List<DBV> dbvs) { currentScenario =
	 * dbvService.getDBVs(dbvs.get(0).getStamp()); if (currentScenario != null
	 * && !currentScenario.isEmpty()) {
	 * LOG.error(THIS_METHOD_MUST_BE_USED_TO_SET_A_WORKSPACE_TO_BE_PERSISTED);
	 * throw new
	 * DBVException(THIS_METHOD_MUST_BE_USED_TO_SET_A_WORKSPACE_TO_BE_PERSISTED
	 * ); } }
	 */

	/*
	 * public List<DBV> loadScenario(String stamp) { currentScenario =
	 * dbvService.getDBVs(stamp); if (currentScenario == null ||
	 * currentScenario.isEmpty()) { LOG.error(WORKSPACE_NOT_FOUND); throw new
	 * DBVException(WORKSPACE_NOT_FOUND); } checkCurrentScenario(); return
	 * currentScenario; }
	 */

	/*
	 * public List<DBV> loadScenario(List<DBV> dbvs) { if (dbvs == null ||
	 * dbvs.isEmpty()) { LOG.error(SCENARIO_NULL); throw new
	 * DBVException(SCENARIO_NULL); } currentScenario = dbvs; return
	 * currentScenario; }
	 */

	/*
	 * Create a new version from the DBV set as current
	 * 
	 * @Override public void commit() { checkCurrentScenario(); currentScenario
	 * = createScenarioFrom(currentScenario); }
	 */

	/*
	 * if (!dbvService.getDBVs(currentScenario.get(0).getStamp()).isEmpty()) {
	 * LOG.warn(WORKSPACE_ALREADY_COMMITED); throw new
	 * DBVException(WORKSPACE_ALREADY_COMMITED); }
	 */

	/*
	 * if (dbvService.getDBVs(currentScenario.get(0).getStamp()) != null) {
	 * LOG.warn(WORKSPACE_ALREADY_COMMITED); throw new
	 * DBVException(WORKSPACE_ALREADY_COMMITED); }
	 */
	/*
	 * LOG.info(COMMITING_WORKSPACE_WITH_STAMP +
	 * currentScenario.get(0).getStamp());
	 * dbvService.getDAO().persist(currentScenario);
	 */

	/*
	 * private void checkCurrentScenario() { if (currentScenario == null ||
	 * currentScenario.isEmpty()) {
	 * LOG.error(YOU_MUST_SET_CURRENT_WORKSPACE_FIRST); throw new
	 * DBVException(YOU_MUST_SET_CURRENT_WORKSPACE_FIRST); } }/* for (DBV dbv :
	 * currentScenario) { if (dbv.getId() == 0L) {
	 * LOG.error(YOU_MUST_PERSIST_THIS_DBV_BEFORE + dbv); throw new
	 * DBVException(YOU_MUST_PERSIST_THIS_DBV_BEFORE + dbv); } }
	 */
	/*
	 * 
	 * private List<DBV> getParentScenario() { checkCurrentScenario(); List<DBV>
	 * parentScenario = new ArrayList<DBV>(); for (DBV current :
	 * currentScenario) { DBV parent = current.getParent(); // if any item is
	 * null, all of them are null if (parent == null) { return null; }
	 * parentScenario.add(parent); } return parentScenario; } private boolean
	 * isThereAnyScenario() { if (currentScenario != null) { return true; } if
	 * (dbvService.getDAO().count() > 0) { return true; } return false; }
	 */

}
